package com.jsbs.iam.ident.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.jsbs.iam.common.core.dto.Result;
import com.jsbs.iam.common.core.utils.Json;
import com.jsbs.iam.common.core.utils.PageHelperTool;
import com.jsbs.iam.common.redis.service.RedisUtils;
import com.jsbs.iam.ident.constant.RedisConstants;
import com.jsbs.iam.ident.dto.IamIdentApplySystemDto;
import com.jsbs.iam.ident.dto.IamPasswordStrategyDto;
import com.jsbs.iam.ident.dto.PasswordStrategySearchDto;
import com.jsbs.iam.ident.entity.IamLoginStrategy;
import com.jsbs.iam.ident.entity.IamPasswordStrategy;
import com.jsbs.iam.ident.feign.IdentFeignInter;
import com.jsbs.iam.ident.mapper.IamLoginStrategyMapper;
import com.jsbs.iam.ident.mapper.IamPasswordStrategyMapper;
import com.jsbs.iam.ident.service.IIamPasswordStrategyService;
import com.jsbs.iam.ident.vo.IamPasswordStrategyVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author zhouxiangyang
 * @className PasswordStrategyImpl
 * @date 2022/12/9 13:27
 * @description:
 */
@Service
@Slf4j
public class PasswordStrategyImpl implements IIamPasswordStrategyService {

    private static final Logger logger = LoggerFactory.getLogger(PasswordStrategyImpl.class);

    @Autowired
    private IamPasswordStrategyMapper iamPasswordStrategyMapper;

    @Resource
    private RedissonClient redissonClient;

    @Autowired
    private IamLoginStrategyMapper iamLoginStrategyMapper;

    @Autowired
    private IdentFeignInter identFeignInter;

    @Value("${_env.name}")
    private String envName;

    @Override
    public PageInfo<IamPasswordStrategyVo> getIamPasswordStrategyList(PasswordStrategySearchDto pageDto,
                                                                      String companyCode) {
        //默认分页
        pageDto.init();
        PageHelper.startPage(pageDto.getCurrPage(),pageDto.getPageSize());
        //查询所有密码策略
        List<IamPasswordStrategy> list
                = iamPasswordStrategyMapper.getIamPasswordStrategyList(pageDto.getStrategyNameOrCode(),companyCode);
        PageInfo<IamPasswordStrategy> page = new PageInfo<>(list);
        list.forEach(iamPasswordStrategy ->{
            String applyTypeCode = iamPasswordStrategy.getApplyTypeCode();
            String applyTypeName = getApplyTypeName(applyTypeCode);
            //展示的时候展示应用名称
            iamPasswordStrategy.setApplyTypeCode(applyTypeName);
        });

        //entity转换vo
        List<IamPasswordStrategyVo> iamPasswordStrategyVos
                = convertIamPasswordStrategyVo(list);
        PageInfo<IamPasswordStrategyVo> pageInfo = new PageInfo<>(iamPasswordStrategyVos);
        //数据分页
        PageInfo<IamPasswordStrategyVo> PageInfoVo
                = PageHelperTool.initPageInfoObj(pageDto.getCurrPage(),
                (int) page.getTotal(), pageDto.getPageSize(), pageInfo);
        return PageInfoVo;
    }

    private List<IamPasswordStrategyVo> convertIamPasswordStrategyVo (List<IamPasswordStrategy> list){
        List<IamPasswordStrategyVo> IamPasswordStrategyVoList = new ArrayList<>();
        list.forEach(IamPasswordStrategy ->{
            IamPasswordStrategyVo iamPasswordStrategyVo = new IamPasswordStrategyVo();
            BeanUtils.copyProperties(IamPasswordStrategy,iamPasswordStrategyVo);
            Date createTime = IamPasswordStrategy.getCreateTime();
            //创建时间转换成String
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String strDate = sdf.format(createTime);
            iamPasswordStrategyVo.setCreateTime(strDate);
            IamPasswordStrategyVoList.add(iamPasswordStrategyVo);
        });
        return IamPasswordStrategyVoList;
    }

    @Override
    public int deleteBatchStrategyCode(List<String> strategyCodeList,String companyCode) {
        int i = iamPasswordStrategyMapper.updateBatchDeleted(strategyCodeList,companyCode);
        return i;
    }

    @Override
    public int saveIamPasswordStrategy(IamPasswordStrategyDto iamPasswordStrategyDto) throws Exception {

        if (iamPasswordStrategyDto == null) {
            throw new Exception("新增密码策略时，入参不能为空");
        }
        if (StringUtils.isEmpty(iamPasswordStrategyDto.getApplyTypeCode())) {
            throw new Exception("应用系统不能为空");
        }
        if (StringUtils.isEmpty(iamPasswordStrategyDto.getPasswordStrategyName())){
            throw new Exception("密码策略名称不能为空");
        }
        //密码长度
        Integer passwordLength = iamPasswordStrategyDto.getPasswordLength();
        //密码包含字母数
        Integer passwordLetterNumber = iamPasswordStrategyDto.getPasswordLetterNumber();
        //密码包含特殊字符个数
        Integer passwordCharNumber = iamPasswordStrategyDto.getPasswordCharNumber();
        //密码包含数字个数
        Integer passwordFigureNumber = iamPasswordStrategyDto.getPasswordFigureNumber();
        //密码包含字母，字符，数字个数不能超过密码长度
        if ((passwordLetterNumber+passwordCharNumber+passwordFigureNumber) >= passwordLength) {
            throw new Exception("当前设置超过密码长度，请修改");
        }
        // 新增密码策略时，策略编码加锁，失效时间10秒钟
        RLock lock = redissonClient.getLock(RedisConstants.IDENT_INFO_CODE_LOCK);
        Long code = null;
        try {
            // 5分钟内重试获取锁，10秒后释放
            boolean isLocked = lock.tryLock(5 * 60 * 1000L, 10 * 1000L, TimeUnit.MILLISECONDS);
            // 获取到锁后，进行自增
            if (!isLocked) {
                log.warn("Route=>PasswordStrategyImpl method=>saveIamPasswordStrategy 新增密码策略时策略编码生成失败:{}",
                        Json.toJsonString(iamPasswordStrategyDto));
                throw new Exception();
            }
            // 加锁成功后获取当前缓存值
            String value = RedisUtils.get(envName + RedisConstants.IDENT_INFO_CODE);
            // 若为空则从数据库取当前最大值，并加新增用户数量入缓存
            if (StringUtils.isEmpty(value)) {
                code = Long.valueOf(iamPasswordStrategyMapper.getPasswordStrategyCode()) + 1;
            } else {
                // 当前缓存获取成功，并加新增用户数量入缓存
                code = Long.valueOf(value) + 1;
            }
            // 将最新值更新到缓存中
            RedisUtils.set(envName + RedisConstants.IDENT_INFO_CODE, String.valueOf(code));
        } catch (Exception e) {
            e.printStackTrace();
            log.error("Route=>PasswordStrategyImpl method=>saveIamPasswordStrategy 新增密码策略时策略编码生成失败:{}",
                    Json.toJsonString(iamPasswordStrategyDto));
            throw new RuntimeException("新增密码策略异常");
        } finally {
            // 手动解锁
            if (lock.isLocked()) {
                lock.unlock();
            }
        }
        AtomicReference<Long> finalCode = new AtomicReference<>(code);
        String applyName = iamPasswordStrategyDto.getApplyTypeCode();
        //根据应用系统名称获取应用系统编码
        String applyTypeCode = getApplyTypeCode(applyName);
        //保存时应存储应用类型
        iamPasswordStrategyDto.setApplyTypeCode(applyTypeCode);
        //校验策略名称和应用编码是否重复
        convertCheckStrategy(iamPasswordStrategyDto);
        iamPasswordStrategyDto.setCreateTime(new Date());
        //校验完毕进行策略新增
        IamPasswordStrategy iamPasswordStrategy
                = convertIamPasswordStrategy(iamPasswordStrategyDto);
        iamPasswordStrategy.setPasswordStrategyCode("CN" + String.format("%04d", finalCode.getAndSet(finalCode.get())));
        return iamPasswordStrategyMapper.insertSelective(iamPasswordStrategy);
    }

    @Override
    public int updateIamPasswordStrategy(IamPasswordStrategyDto iamPasswordStrategyDto) throws Exception {

        String applyName = iamPasswordStrategyDto.getApplyTypeCode();
        if (StringUtils.isNotEmpty(applyName)) {
            //根据应用系统名称获取应用系统编码
            String applyTypeCode = getApplyTypeCode(applyName);
            iamPasswordStrategyDto.setApplyTypeCode(applyTypeCode);
        }
        //校验策略名称和应用编码是否重复
        convertCheckStrategy(iamPasswordStrategyDto);
        iamPasswordStrategyDto.setUpdateTime(new Date());
        //TODO 更新人
        //校验完毕更新策略
        IamPasswordStrategy iamPasswordStrategy = convertIamPasswordStrategy(iamPasswordStrategyDto);
        return iamPasswordStrategyMapper.updateByPrimaryKeySelective(iamPasswordStrategy);
    }

    @Override
    public List<IamLoginStrategy> getIamLoginStrategy(String applyTypeCode,String companyCode) {
        //根据应用类型编码查询该应用下所有的登录方式
        List<IamLoginStrategy> iamLoginStrategy =
                iamLoginStrategyMapper.getIamLoginStrategy(applyTypeCode,companyCode);
        return iamLoginStrategy;
    }

    @Override
    @Transactional
    public int saveIamLoginStrategy(List<IamLoginStrategy> iamLoginStrategyList) throws Exception {
        Set<String> code = new HashSet<>();
        iamLoginStrategyList.forEach(iamLoginStrategy ->{
            String applyTypeCode = iamLoginStrategy.getApplyTypeCode();
            //取出应用类型
            code.add(applyTypeCode);
        });
        if (code.size()>1) {
            throw new Exception("入参应用类型不一致");
        }
        String applyTypeCode = null;
        for (Iterator iterator = code.iterator(); iterator.hasNext();) {
            applyTypeCode = (String) iterator.next();
        }
        //先删除再新增
        iamLoginStrategyMapper.deleteApplyCode(applyTypeCode);
        int i = iamLoginStrategyMapper.saveIamLoginStrategy(iamLoginStrategyList);
        return i;
    }

    @Override
    public IamPasswordStrategyVo queryPasswordStrategy(String applyTypeCode,String companyCode) {
        IamPasswordStrategy passwordStrategy = iamPasswordStrategyMapper.getApplyTypeCode(applyTypeCode,companyCode);
        IamPasswordStrategyVo iamPasswordStrategyVo = new IamPasswordStrategyVo();
        BeanUtils.copyProperties(passwordStrategy,iamPasswordStrategyVo);
        return iamPasswordStrategyVo;
    }

    private void convertCheckStrategy (IamPasswordStrategyDto iamPasswordStrategyDto) throws Exception{
        //密码策略名称
        String passwordStrategyName = iamPasswordStrategyDto.getPasswordStrategyName();
        String companyCode = iamPasswordStrategyDto.getCompanyCode();
        if (StringUtils.isNotEmpty(passwordStrategyName)) {
            //通过策名称查询
            IamPasswordStrategy iamPasswordStrategy = iamPasswordStrategyMapper.getStrategyName(passwordStrategyName,companyCode);
            if (iamPasswordStrategy != null ){
                String codeDto = iamPasswordStrategyDto.getPasswordStrategyCode();
                String passwordStrategyCode = iamPasswordStrategy.getPasswordStrategyCode();
                //如果入参的策略编码和查询到的策略编码不一致则策略名称重复，进行异常抛出
                if (!codeDto.equals(passwordStrategyCode)) {
                    throw new Exception("密码策略名称重复，请更重新命名密码策略名称");
                }
            }
        }
        //应用系统编码
        String applyTypeCode = iamPasswordStrategyDto.getApplyTypeCode();
        if (StringUtils.isNotEmpty(applyTypeCode)) {
            //根据应用系统编码查询策略
            IamPasswordStrategy iamPasswordStrategyApply = iamPasswordStrategyMapper.getApplyTypeCode(applyTypeCode,companyCode);
            if (iamPasswordStrategyApply != null ){
                String passwordStrategyCode = iamPasswordStrategyApply.getPasswordStrategyCode();
                String applyTypeCodeDto = iamPasswordStrategyDto.getPasswordStrategyCode();
                //根据应用系统查询到的策略编码为空 证明该应用已经绑定了密码策略
//                if (StringUtils.isEmpty(applyTypeCodeDto)) {
//                    throw new Exception("该应用已经绑定了密码策略");
//                }
                //如果入参的策略编码和查询到的策略编码不一致则策略名称重复，进行异常抛出
                if (!applyTypeCodeDto.equals(passwordStrategyCode)) {
                    throw new Exception("该应用已经绑定了密码策略");
                }
            }
        }
    }
    //密码策略的dto转entity
    private IamPasswordStrategy convertIamPasswordStrategy(IamPasswordStrategyDto iamPasswordStrategyDto){
        IamPasswordStrategy iamPasswordStrategy = new IamPasswordStrategy();
        BeanUtils.copyProperties(iamPasswordStrategyDto,iamPasswordStrategy);
        return iamPasswordStrategy;
    }

    private String getApplyTypeCode (String applyName)  {
        IamIdentApplySystemDto iamApplySystemDto = new IamIdentApplySystemDto();
        iamApplySystemDto.setApplyName(applyName);
        //先查应用系统编码
        Result applyTypeCode = identFeignInter.getApplyTypeCode(iamApplySystemDto);
        return  applyTypeCode.getData().toString();
    }

    private String getApplyTypeName (String applyTypeCode)  {
        IamIdentApplySystemDto identApplySystemDto = new IamIdentApplySystemDto();
        identApplySystemDto.setApplyName(applyTypeCode);
        Result applyTypeName = identFeignInter.getApplyTypeName(identApplySystemDto);
        Object data = applyTypeName.getData();
        if (data != null) {
            return  data.toString();
        } else {
            logger.info("应用类型没有对应的名称数据，使用应用类型返回{}",applyTypeCode);
            return applyTypeCode;
        }
    }

}
